package service

import (
	"context"
	"errors"
	"iris-wms/apps/admin/web/dto/din"
	"iris-wms/apps/admin/web/dto/dout"
	"iris-wms/common/model"
	"iris-wms/common/pkg/db"
	"iris-wms/common/pkg/db/dao"
	"iris-wms/common/tools/consts"

	"gorm.io/gen"
	"gorm.io/gorm"
)

var _ WmsBillService = (*wmsBillService)(nil)

type WmsBillService interface {
	Create(din.WmsBillIn, consts.BillMark) error
	Update(din.WmsBillIn, consts.BillMark) error
	DeleteByIdList([]uint) error
	GetTable(din.WmsBillTableIn) ([]*dout.WmsBillOut, int64, error)
	FindByCode(string, consts.WorkType) (*dout.WmsBillOut, error)
}

func NewWmsBillService() *wmsBillService {
	return &wmsBillService{}
}

type wmsBillService struct{}

func (impl *wmsBillService) Create(in din.WmsBillIn, mark consts.BillMark) (err error) {
	inventoryService := NewWmsInventoryService()
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		invtList := make([]*model.WmsInventory, 0)
		for _, v := range in.GoodsList {
			if mark == consts.BillMarkOut {
				// 检查库存，如果库区库存不足，不能出货
				inventory, err := inventoryService.QueryInventory(v.GoodsId, v.Wid)
				if err != nil {
					if errors.Is(err, gorm.ErrRecordNotFound) {
						return consts.Wms_Bill_Ountbound_Not_Qty
					}
					return err
				}
				if inventory.SumQty < v.Qty {
					return consts.Wms_Bill_Ountbound_Not_Qty
				}
			}
			v.BillId = in.Bill.ID
			v.Mark = mark
			// invt
			invtList = append(invtList, &model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.Wid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    mark,
				IsGift:  v.IsGift,
			})
		}
		if err = tx.CreateInBatches(&in.GoodsList, len(in.GoodsList)).Error; err != nil {
			return err
		}
		if len(invtList) > 0 {
			if err = tx.CreateInBatches(&invtList, len(invtList)).Error; err != nil {
				return err
			}
		}
		//
		if in.Bill.WorkType == consts.WorkType2 && len(in.LogisticsList) > 0 {
			for _, v := range in.LogisticsList {
				v.BillId = in.Bill.ID
			}
			if err = tx.CreateInBatches(&in.LogisticsList, len(in.LogisticsList)).Error; err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillService) Update(in din.WmsBillIn, mark consts.BillMark) (err error) {
	inventoryService := NewWmsInventoryService()
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsBillGoods{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsInventory{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsBillLogistics{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		for _, v := range in.GoodsList {
			if mark == consts.BillMarkOut {
				inventory, err := inventoryService.QueryInventory(v.GoodsId, v.Wid)
				if err != nil {
					if errors.Is(err, gorm.ErrRecordNotFound) {
						return consts.Wms_Bill_Ountbound_Not_Qty
					}
					return err
				}
				if inventory.SumQty < v.Qty {
					return consts.Wms_Bill_Ountbound_Not_Qty
				}
			}

			v.BillId = in.Bill.ID
			v.Mark = mark
			if err = tx.Save(&v).Error; err != nil {
				return err
			}
			// invt
			invt := model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.Wid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    mark,
				IsGift:  v.IsGift,
			}
			if err = tx.Save(&invt).Error; err != nil {
				return err
			}
		}
		//
		if in.Bill.WorkType == consts.WorkType2 && len(in.LogisticsList) > 0 {
			if err = tx.Unscoped().Delete(&model.WmsBillLogistics{}, "bill_id=?", in.Bill.ID).Error; err != nil {
				return err
			}
			for _, v := range in.LogisticsList {
				v.BillId = in.Bill.ID
				if err = tx.Save(&v).Error; err != nil {
					return err
				}
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillService) DeleteByIdList(idList []uint) (err error) {
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&model.WmsBill{}, "id in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Model(&model.WmsInventory{}).Where("bill_id in ?", idList).Update("status", 0).Error; err != nil {
			return err
		}
		return nil
	})
	return
}

func (impl *wmsBillService) GetTable(in din.WmsBillTableIn) (rows []*dout.WmsBillOut, total int64, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())

	condList := make([]gen.Condition, 0)
	if in.SupplierId > 0 {
		condList = append(condList, q.SupplierId.Eq(in.SupplierId))
	}
	if in.CustomerId > 0 {
		condList = append(condList, q.CustomerId.Eq(in.CustomerId))
	}
	if in.IoType > 0 {
		condList = append(condList, q.IoType.Eq(in.IoType))
	}
	if in.Word != "" {
		do = do.Where(do.Where(q.Code.Eq(in.Word)).
			Or(q.OrderCode.Eq(in.Word)))
	}

	rows, total, err = do.
		Where(condList...).
		Where(q.WorkType.Eq(int8(in.WorkType))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.Supplier).Preload(q.Customer).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.Warehouse).
		Order(q.CreatedAt.Desc()).
		FindByPage(in.Offset(), in.PageSize)
	return
}

func (impl *wmsBillService) FindByCode(code string, workType consts.WorkType) (bill *dout.WmsBillOut, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())
	bill, err = do.Where(q.Code.Eq(code), q.WorkType.Eq(int8(workType))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.Supplier).Preload(q.Customer).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.Warehouse).
		Preload(q.LogisticsList.Carrier).
		First()
	return
}
